package com.wsg.dep.queue.seq;

import com.wsg.dep.Constant;
import com.wsg.dep.util.UnSafeUtil;
import sun.misc.Unsafe;

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 用于缓存行填充，防止伪共享的类。
 * 此类中的字段用于填充缓存行，使得当前对象在内存中的布局可以避免与其他对象竞争缓存行，
 * 从而减少伪共享带来的性能影响。
 */
class LhsPadding {
    // 缓存行填充字段，7 个 long 类型字段，共 56 字节，加上对象头的一些信息，可基本填满一个缓存行
    protected long p1, p2, p3, p4, p5, p6, p7;

    /**
     * 构造函数，将填充字段初始化为 0L，防止 JVM 优化填充字段。
     */
    public LhsPadding() {
        p1 = p2 = p3 = p4 = p5 = p6 = p7 = 0L;
    }
}

/**
 * 包含实际序列号值的类，继承自 LhsPadding。
 * 由于继承了 LhsPadding，该类的对象在内存中会先有 LhsPadding 的填充字段，再是自身的 value 字段。
 * value 字段使用 volatile 关键字修饰，保证了在多线程环境下的内存可见性。
 */
class Value extends LhsPadding {
    // 表示序列号的值，使用 volatile 关键字保证内存可见性，确保多线程环境下能及时获取最新值
    protected volatile long value;
}

/**
 * 用于进一步缓存行填充的类，继承自 Value。
 * 此类的字段同样用于填充缓存行，避免伪共享问题，增强了多线程环境下的性能表现。
 */
class RhsPadding extends Value {
    // 缓存行填充字段，7 个 long 类型字段，共 56 字节，进一步填充缓存行
    protected long p9, p10, p11, p12, p13, p14, p15;

    /**
     * 构造函数，将填充字段初始化为 0L，防止 JVM 优化填充字段。
     */
    public RhsPadding() {
        p9 = p10 = p11 = p12 = p13 = p14 = p15 = 0L;
    }
}

/**
 * 实现了一个序列号生成器的类，使用无锁原子操作和缓存行填充技术。
 * 该类通过继承 RhsPadding 类，利用缓存行填充来避免伪共享，同时借助 Unsafe 类提供的
 * 低级别操作实现了无锁原子的序列号递增和设置等功能，适用于多线程环境下的序列号生成场景。
 */
public class Seq extends RhsPadding {

    // 用于执行底层的内存操作，提供了直接访问内存等低级别操作的能力
    private static final Unsafe UNSAFE;

    // value 字段在对象内存中的相对偏移位置，用于通过 Unsafe 类访问 value 字段。
    // 每个 Seq 实例都有自己的内存空间，VALUE_INDEX 只是表示 value 字段相对于每个实例对象内存起始位置的偏移量，所以不同实例对象的 value 值存储在不同的内存位置上，通过各自的偏移量来访问和操作。
    private static final long VALUE_INDEX;

    /**
     * 静态代码块，在类加载时执行，初始化 UNSAFE 和 VALUE_INDEX。
     * 其中通过 UnSafeUtil.getUnsafe() 方法获取 Unsafe 实例，并尝试获取
     * Value 类中 value 字段在内存中的偏移位置，如果获取失败则抛出运行时异常。
     */
    static {
        UNSAFE = UnSafeUtil.getUnsafe();
        try {
            VALUE_INDEX = UNSAFE.objectFieldOffset(Value.class.getDeclaredField("value"));
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 无参构造函数，调用带参构造函数并传入初始值 Constant.INITIAL_VALUE，
     * 用于初始化序列号生成器的初始序列号值。
     */
    public Seq() {
        this(Constant.INITIAL_VALUE);
    }

    /**
     * 带参构造函数，用于初始化序列号的值。
     *
     * @param initialValue 初始化序列号的值
     */
    public Seq(final long initialValue) {
        // 使用 Unsafe 类将 initialValue 写入到 VALUE_INDEX 偏移位置所对应的内存中
        UNSAFE.putOrderedLong(this, VALUE_INDEX, initialValue);
    }

    /**
     * 获取当前序列号的值。
     *
     * @return 当前序列号的值
     */
    public long get() {
        return value;
    }

    /**
     * 设置序列号的值，不保证其他线程的可见性，性能略优于 putLongVolatile。
     *
     * @param value 要设置的序列号的新值
     */
    public void set(final long value) {
        UNSAFE.putOrderedLong(this, VALUE_INDEX, value);
    }

    /**
     * 设置序列号的值，写入时保持 volatile 的特性，保证其他线程的可见性，性能略低于 putOrderedLong。
     *
     * @param value 要设置的序列号的新值
     */
    public void setVolatile(final long value) {
        UNSAFE.putLongVolatile(this, VALUE_INDEX, value);
    }

    /**
     * 将当前对象中存储的序列值与预期值进行比较并更新。
     * 如果当前序列值等于预期值 expectedValue，就将序列值更新为 newValue，并返回 true；
     * 若不相等，则不进行更新操作，直接返回 false。
     *
     * @param expectedValue 预期的序列值（旧值）
     * @param newValue      要更新的新序列值
     * @return 相等返回 true，不相等返回 false
     */
    public boolean compareAndSet(final long expectedValue, final long newValue) {
        return UNSAFE.compareAndSwapLong(this, VALUE_INDEX, expectedValue, newValue);
    }

    /**
     * 当前序列号加 1。
     *
     * @return 加 1 后的序列号值
     */
    public long incrementAndGet() {
        return addAndGet(1L);
    }

    /**
     * 在当前的序列号上增长一个长度。
     *
     * @param increment 要增长的长度值
     * @return 增长后的序列号值
     */
    public long addAndGet(final long increment) {
        long currentValue;
        long newValue;

        do {
            currentValue = get();
            newValue = currentValue + increment;
        } while (!compareAndSet(currentValue, newValue));

        return newValue;
    }

    /**
     * 将序列号的值转换为字符串形式返回。
     *
     * @return 序列号值的字符串表示
     */
    @Override
    public String toString() {
        return Long.toString(get());
    }

    /**
     * 主方法，用于测试序列号生成器在多线程环境下的功能。
     * 此方法创建了一个序列号生成器实例，一个固定大小的线程池，以及一个 CyclicBarrier。
     * 然后提交多个线程任务，每个线程在 barrier 处等待，接着执行 10000 次序列号递增操作。
     * 最后关闭线程池并等待任务执行完毕，输出最终的序列号值，预期值为 40000（4 个线程，每个线程递增 10000 次）。
     *
     * @param args 命令行参数，在此方法中未使用
     * @throws InterruptedException 当线程在等待过程中被中断时抛出
     */
    public static void main(String[] args) throws InterruptedException {
        // 创建一个 Seq 实例，即序列号生成器实例
        final Seq sequence = new Seq();
        // 定义线程数量
        final int threadCount = 4;
        // 创建一个固定大小的线程池，线程池大小为 threadCount
        final ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        // 创建一个 CyclicBarrier，用于让多个线程在某个点同步
        final CyclicBarrier barrier = new CyclicBarrier(threadCount);

        // 提交多个线程任务，每个线程在 barrier 处等待，然后执行 10000 次序列号递增操作
        for (int i = 0; i < threadCount; i++) {
            executor.submit(() -> {
                try {
                    barrier.await();
                    for (int j = 0; j < 10_000; j++) {
                        sequence.incrementAndGet();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            });
        }

        // 关闭线程池，不再接受新任务
        executor.shutdown();
        // 等待线程池中的任务执行完毕，最多等待 1 分钟
        executor.awaitTermination(1, TimeUnit.MINUTES);
        // 输出最终的序列号值，预期值为 40000（4 个线程，每个线程递增 10000 次）
        // 由于从-1开始递增，故最终值为40000-1
        System.out.println("Final value: " + sequence.get());
    }
}